#include "guish.hpp"
#include <errno.h>

PtyManager *ptyManager = NULL;

PtyManager::PtyManager()
{
	pthread_mutex_init(&lock, NULL);
}

PtyManager::~PtyManager()
{
	pthread_mutex_destroy(&lock);
}

PseudoTerminal *PtyManager::getNewPty(StreamSender *outputHandler, int width, int height)
{
	pthread_mutex_lock(&lock);
	PseudoTerminal *pty = new PseudoTerminal(width, height);
	ptys[pty] = outputHandler;
	pthread_mutex_unlock(&lock);
	eventManager->notify();
	return pty;
}

void PtyManager::stopPty(PseudoTerminal *pty)
{
	pthread_mutex_lock(&lock);
		ptys.erase(pty);
	pthread_mutex_unlock(&lock);
}

void PtyManager::restartPty(PseudoTerminal *pty, StreamSender *outputHandler)
{
	pthread_mutex_lock(&lock);
		ptys[pty] = outputHandler;
	pthread_mutex_unlock(&lock);
	eventManager->notify();
}

void PtyManager::discardPty(PseudoTerminal *pty)
{
	pthread_mutex_lock(&lock);
	map<PseudoTerminal*,StreamSender*>::iterator it = ptys.find(pty);
	
	if(it != ptys.end())
	{
		StreamSender *sender = it->second;
		if(sender) delete sender;
		
		ptys.erase(it);
		delete pty;
	}
	pthread_mutex_unlock(&lock);
}

void PtyManager::prepareSelect(int priority, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, int *maxfd)
{
	pthread_mutex_lock(&lock);
	for(map<PseudoTerminal*,StreamSender*>::iterator ii=ptys.begin(); ii!=ptys.end(); ii++)
	{
		PseudoTerminal *pty = ii->first;
		int fd = pty->getMasterFd();
		
		if(pty->disabled)
			continue;
		
		if(exceptfds)
			FD_SET(fd, exceptfds);
		if(writefds && pty->hasPendingInput())
			FD_SET(fd, writefds);
		if(readfds && priority>=2)
			FD_SET(fd, readfds);
		
		if(*maxfd < fd)
			*maxfd = fd;
	}
	pthread_mutex_unlock(&lock);
}

void PtyManager::postSelect(int priority, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
{
	pthread_mutex_lock(&lock);
	vector<PseudoTerminal*> deadPtys;
	
	for(map<PseudoTerminal*,StreamSender*>::iterator ii=ptys.begin(); ii!=ptys.end(); ii++)
	{
		PseudoTerminal *pty = ii->first;
		int fd = pty->getMasterFd();
		
		if(FD_ISSET(fd, writefds)) {
			pty->pushInput();
		}
		
		if(FD_ISSET(fd, readfds) || FD_ISSET(fd, exceptfds))
		{
			StreamSender *sender = ii->second;
			char buf[1024];
			
			int bytesRead = read(fd, buf, 1024);
			
			if(bytesRead > 0) {
				sender->send(bytesRead, buf);
			} else if(bytesRead == 0) {
				deadPtys.push_back(pty);
			} else { //bytesRead<0
				if(errno != EAGAIN && errno != EINTR) {
					string errmsg = strerror(errno);
					deadPtys.push_back(pty);
				}
			}
		}
	}
	
	for(size_t ii=0; ii<deadPtys.size(); ii++)
	{
		deadPtys[ii]->disabled = true;
	}
	pthread_mutex_unlock(&lock);
}

void PtyManager::readAllPty(PseudoTerminal *pty)
{
	pthread_mutex_lock(&lock);
	if(ptys.find(pty)==ptys.end()) {
		pthread_mutex_unlock(&lock);
		return;
	}
	
	char buf[1024];
	int bytesRead = 0;
	int fd = pty->getMasterFd();
	
	StreamSender *sender = ptys[pty];
	
	do {
		bytesRead = read(fd, buf, 1024);
		
		if(bytesRead > 0) {
			sender->send(bytesRead, buf);
		}
		
	} while(bytesRead > 0);
	pthread_mutex_unlock(&lock);
}

void PtyManager::resizeAll(int width, int height)
{
	pthread_mutex_lock(&lock);
	for(map<PseudoTerminal*, StreamSender*>::iterator ii=ptys.begin(); ii!=ptys.end(); ii++)
	{
		PseudoTerminal *pty = ii->first;
		pty->setSize(width, height);
	}
	// TODO? Send SIGNWINCHes
	pthread_mutex_unlock(&lock);
}

